﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SSS.AlphaNN.Logic
{
    /// <summary>
    /// Этот метагенетический алгоритм
    /// </summary>
    /// <typeparam name="TypeUser">Желательно что бы этот тип был со сравнением его экземпляров</typeparam>
    public class GenericGeneticAlg<TypeUser>
    {
        #region Properties
        /// <summary>
        /// Скрещивание двух особей
        /// </summary>
        /// <param name="genOne">Ген первой особи</param>
        /// <param name="genTwo">Ген второй особи</param>
        /// <returns>Возвращает значение</returns>
        public List<Func<List<TypeUser>, List<TypeUser>, List<TypeUser>>> _Crossingover;

        /// <summary>
        /// Мутация гена
        /// </summary>
        /// <param name="gen">вводим ген</param>
        /// <returns>Возвращает мутанта</returns>
        public List<Func<List<TypeUser>, List<TypeUser>>>  _Mutation;

        /// <summary>
        /// Функция которой вычисляется оптимум
        /// </summary>
        /// <param name="gen">Вводим ген</param>
        /// <returns>Результат функции</returns>
        public Func<List<TypeUser>, TypeUser> _FunctionForOptimum;

        /// <summary>
        /// Функция вычисляет номер оптимального значения
        /// </summary>
        /// <param name="gens">Ген</param>
        /// <returns>Возвращает номер самой живучей твари;-)</returns>
        public Func<List<TypeUser>, int> _Optimum;

        /// <summary>
        /// Первый выбор гена
        /// </summary>
        /// <returns>Возвращает первое приближение</returns>
        public Func<List<TypeUser>> _FirstStep;
       
        

        /// <summary>
        /// Количество проходов
        /// </summary>
        public int _steps;


        /// <summary>
        /// Количество переменных
        /// </summary>
        public int _CountArgs;

        /// <summary>
        /// Количество первых приближений
        /// </summary>
        public int _NumberFirstApproximation;

        /// <summary>
        /// Количество раз использование кроссовера
        /// </summary>
        public int _NumberUsingCrossover;

        /// <summary>
        /// Количество использования мутаций
        /// </summary>
        public int _NumberUsingMutation;
        #endregion

        #region Constructor

        private void Const()
        {
            _steps=10;
            _Crossingover =new List<Func<List<TypeUser>, List<TypeUser>, List<TypeUser>>>();
            _Mutation=new List<Func<List<TypeUser>, List<TypeUser>>>();
            _NumberFirstApproximation=2;
            _NumberUsingCrossover = 2;
        }

        public GenericGeneticAlg( )
        {
            Const();
        }

      

        #endregion

        /// <summary>
        /// Эта функция отвечает за генерацию чистых поколений
        /// </summary>
        /// <param name="menX"></param>
        protected virtual void FirstApproximation(int n, ref List<List<TypeUser>> menX)
        {
            //Вот здесь мы создаём "первое" поколение
            for ( int i = 0; i < _NumberFirstApproximation; i++ )
            {
                menX.Add( GA( n - 1 ) );
            }
        }

        /// <summary>
        /// Эта процедура отвечает за скрещивание особей
        /// </summary>
        /// <param name="menX"></param>
        protected virtual void CrossoverGlobal(ref  List<List<TypeUser>> menX)
        {
            //индексы для определения номеров генов которые будут участвовать в кроссовере
            int indexGenF=0;
            int indexGenS=1;
            //сам кроссовер
            for ( int i = 0; i < _NumberUsingCrossover; i++ )
            {
                for ( int j=0; j<_Crossingover.Count; j++ )
                {
                    menX.Add( _Crossingover[j]( menX[indexGenF], menX[indexGenS] ) );
                    Swap( ref indexGenF, ref indexGenS );
                }

            }
        }

        /// <summary>
        /// Мутация для всех поколений
        /// </summary>
        /// <param name="menX"></param>
        protected virtual void MutationGlobal(ref List<List<TypeUser>> menX)
        {
            int c=menX.Count;
            for ( int i=0; i<c; i++ )
            {
                for ( int j=0; j<_NumberUsingMutation; j++ )
                {
                    for ( int k = 0; k < _Mutation.Count; k++ )
                    {
                        menX.Add( _Mutation[k]( menX[i] ) );
                    }
                }
            }

        }

        protected virtual int FindNumberOptimums(List<List<TypeUser>> menX)
        {
            Queue<TypeUser> qu=new Queue<TypeUser>();
            try
            {
                foreach ( var a in menX )
                {
                    TypeUser ty=_FunctionForOptimum(a);
                    qu.Enqueue(ty);
                }
            }
            catch
            {
                ;
            }

            if (qu.Count>0)
            {
                return _Optimum( (new List<TypeUser>(qu)) );
            }
            else
            {
                return 0;
            }
        }

        protected virtual List<TypeUser> GA( int n ) 
        {
            if ( n==0 )
            {
                return _FirstStep();
            }
            else
            {
                List<List<TypeUser>> menX = new List<List<TypeUser>>();

                FirstApproximation(n, ref menX );

                int count = menX.Count;

                menX = menX.Distinct().ToList();

                while(menX.Count<count)
                {
                    MutationGlobal(ref menX);
                }


                CrossoverGlobal( ref menX );


                MutationGlobal( ref menX );

                return menX[FindNumberOptimums(menX)];
            }
        }

        public virtual List<TypeUser> GeneticAlgorithm()
        {
            if ( _Crossingover!=null&&_Mutation!=null&&_FunctionForOptimum!=null )
            {
                return GA( _steps );
            }
            else
            {
                throw new ArgumentNullException( "You have not specified functions " );
            }
        }


        #region Code Gray
        private static List<int[]> codes;

        private static void RecursivCodeGray( int[] b1, int inv )
        {
            int len=b1.Length;
            for ( int j=len-1; j>inv; RecursivCodeGray( b1, j-- ) )
            {
                b1[j]=( ( b1[j]+1 )%2 );
                codes.Add( (int[])b1.Clone() );
            }
        }

        public static List<int[]> GenerationCodeGray(int n)
        {
            int[] firstStep=new int[n];
            RecursivCodeGray(firstStep, -1);

            return codes;
        }

        public static void Swap(ref int X,ref int Y)
        {
            X^=Y;
            Y^=X;
            X^=Y;
        }
        #endregion
    }
}
